package com.apes.framework.plugin.fileserver.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.SpringManager;
import com.apes.framework.plugin.fileserver.domain.File;
import com.apes.framework.plugin.fileserver.service.FileService;
import com.apes.framework.plugin.fileserver.util.MD5Util;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.StringUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

/**
 * 功能：File Controller
 *
 * @author xul
 * @create 2018-05-25 14:38
 */
@CrossOrigin(origins = "*", maxAge = 3600)  // 允许所有域名访问
@Controller
public class FileController {

    @Autowired
    private FileService fileService;

    @Value("${fileserver.upload.allow:image/gif,image/jpeg,image/png,application/pdf}")
    private String uploadAllow;

    /**
     * 分页查询文件
     *
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @GetMapping("/fileserver/files/{pageIndex}/{pageSize}")
    @ResponseBody
    public List<File> listFilesByPage(@PathVariable int pageIndex, @PathVariable int pageSize) {
        return fileService.listFilesByPage(pageIndex, pageSize);
    }

    /**
     * 获取文件
     *
     * @param id
     * @return
     */
    @GetMapping("/fileserver/files/{id}")
    @ResponseBody
    public ResponseEntity<Object> serveFile(@PathVariable String id) throws Exception {
        File file = fileService.getFileById(id);
        if (file != null) {
            return ResponseEntity
                    .ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\"")
                    .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
                    .header(HttpHeaders.CONTENT_LENGTH, file.getSize() + "")
                    .header("Connection", "close")
                    .body(file.getContent());
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount !");
        }
    }

    /**
     * 打包下载多个文件
     *
     * @param body
     * @return
     */
    @PostMapping(value = "/fileserver/download", produces = {"text/html;charset=utf-8"})
    @ResponseBody
    public ResponseEntity<Object> downFilesToZip(@RequestParam("body") String body) throws Exception {
        JSONArray fileMetes;
        try {
            fileMetes = JSON.parseArray(body);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("请求输入参数格式错误！".getBytes("UTF-8"));
        }
//        if (fileMetes.size() == 1) {
//            return serveFile(((JSONObject) fileMetes.get(0)).getString("id"));
//        }
        try (FastByteArrayOutputStream out = new FastByteArrayOutputStream()) {
            filesToZip(fileMetes, out);
            return ResponseEntity
                    .ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\"" + URLEncoder.encode(DateUtil.format("yyyyMMddHHmmss") + ".zip", "UTF-8") + "\"")
                    .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
                    .header(HttpHeaders.CONTENT_LENGTH, out.size() + "")
                    .header("Connection", "close")
                    .body(out.toByteArray());
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(("批量文件下载失败！" + e.getMessage()).getBytes("UTF-8"));
        }
    }

    /**
     * 多个文件压缩成zip
     *
     * @param fileMetes 文件列表
     * @param out       输出流
     */
    private void filesToZip(JSONArray fileMetes, OutputStream out) {
        try (ZipOutputStream zos = new ZipOutputStream(out)) {
            zos.setEncoding("GBK");
            for (Object fileMete : fileMetes) {
                String id = ((JSONObject) fileMete).getString("id");
                if (StringUtils.isEmpty(id)) continue;
                File file = fileService.getFileById(id);
                if (file == null) {
                    continue;
                }
                zos.putNextEntry(new ZipEntry(file.getName()));
                zos.write(file.getContent());
                zos.closeEntry();
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 在线显示文件
     *
     * @param id
     * @return
     */
    @GetMapping("/fileserver/view/{id}")
    @ResponseBody
    public ResponseEntity<Object> serveFileOnline(@PathVariable String id) throws Exception {
        File file = fileService.getFileById(id);
        if (file != null) {
            return ResponseEntity
                    .ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\"")
                    .header(HttpHeaders.CONTENT_TYPE, file.getContentType())
                    .header(HttpHeaders.CONTENT_LENGTH, file.getSize() + "")
                    .header("Connection", "close")
                    .body(file.getContent());
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount !");
        }
    }


    /**
     * 上传
     *
     * @param files
     * @return id
     */
    @PostMapping(value = "/fileserver/upload", produces = {"text/html;charset=utf-8"})
    @ResponseBody
    public ResponseEntity<String> handleFileUpload(@RequestParam("files") MultipartFile[] files) {
        try {
            List result = new ArrayList();
            for (MultipartFile file : files) {
                if (!uploadAllow.contains(file.getContentType())) {
                    throw new RuntimeException("系统禁止：" + file.getContentType() + " 类型的文件上传！");
                }
            }

            for (MultipartFile file : files) {
                File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(), file.getBytes());
                f.setMd5(MD5Util.getMD5(file.getInputStream()));
                File returnFile = fileService.saveFile(f);
                result.add(MapUtil.mapper("id", returnFile.getId(),
                        "type", returnFile.getContentType(),
                        "size", returnFile.getSize(),
                        "name", returnFile.getName(),
                        "fileName", file.getOriginalFilename()));
            }
            //URLEncoder.encode(, "UTF-8")))
            return ResponseEntity.status(HttpStatus.OK).body(JSON.toJSONString(result));
        } catch (IOException | NoSuchAlgorithmException ex) {
            ex.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
        }
    }

    /**
     * 删除文件
     *
     * @param id
     * @return
     */
    @GetMapping("/fileserver/delete/{id}")
    @ResponseBody
    public ResponseEntity<String> deleteFile(@PathVariable String id) {
        try {
            fileService.removeFile(id);
            return ResponseEntity.status(HttpStatus.OK).body("Delete Success!");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    /**
     *
     * 在线显示第三方文件
     */
    @GetMapping("/file/view")
    @ResponseBody
    public ResponseEntity<Object> view(@RequestParam(value = "source") String source,
                                       @RequestParam(value = "path") String path) throws Exception {
        String url = StringUtil.getUrl(source, path);
        byte[] bytes = null;
        if (url != null) {
            bytes = downloadImage(url);
        }
        if (bytes != null) {
            String fileName = path.substring(path.lastIndexOf("/") + 1);
            String contentType = fileName.substring(fileName.lastIndexOf(".") + 1);
            return ResponseEntity
                    .ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"")
                    .header(HttpHeaders.CONTENT_TYPE, "image/" + contentType)
                    .header(HttpHeaders.CONTENT_LENGTH, bytes.length + "")
                    .header("Connection", "close")
                    .body(bytes);
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount !");
        }
    }

    private byte[] downloadImage(String url) {
        CloseableHttpClient httpclient = SpringManager.getBean(CloseableHttpClient.class);
        HttpGet httpGet = new HttpGet(url);
        RequestConfig requestConfig = SpringManager.getBean(RequestConfig.class);
        httpGet.setConfig(requestConfig);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)");
        try (ByteArrayOutputStream os = new ByteArrayOutputStream();
             CloseableHttpResponse response = httpclient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream is = entity.getContent();
                try {
                    byte b[] = new byte[4096];
                    int i;
                    while ((i = is.read(b)) != -1) {
                        os.write(b, 0, i);
                    }
                    os.flush();
                    return os.toByteArray();
                } catch (IOException ex) {
                    throw ex;
                } catch (RuntimeException ex) {
                    httpGet.abort();
                    throw ex;
                } finally {
                    try {
                        is.close();
                    } catch (Exception ex) {
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }
}
